#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; }

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>"||ls.getSymCour()=="si"||ls.getSymCour()=="tantque" ||ls.getSymCour()=="pour"
                ||ls.getSymCour()=="repeter"||ls.getSymCour()=="lire"||ls.getSymCour()=="ecrire"||ls.getSymCour()=="switch");
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <inst_condi>

     if(ls.getSymCour()=="si") return instSi();
     else if (ls.getSymCour()=="tantque") return instTq();
     else if (ls.getSymCour()=="pour") return instPour();
     else if (ls.getSymCour()=="repeter") return instRepeter();
     else if (ls.getSymCour()=="ecrire") return instEcrire();
     else if (ls.getSymCour()=="lire") return instLire();
     else if (ls.getSymCour()=="switch") return instSwitch();
    else return affectation();
        
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>

	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
	Noeud* exp = expression();
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }

	Noeud* fact = facteur();
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
		Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
		Noeud* factDroit=facteur(); // lecture de l'operande droit
		fact = new NoeudOperateurAdd(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
 // <facteur> ::= <entier> | <variable> | <opUnaire> <expBool> | ( <expBool> )

	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-" || ls.getSymCour()=="non") {
		ls.suivant();
		// on représente le moins unaire (- expBool) par une soustractin binaire (0 - expBool)
		fact = new NoeudOperateurUnaire(Symbole("-"), ts.chercheAjoute(Symbole("0")), expBool());
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expBool();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd() {
//  <opAdd>  ::= + | - 
	Symbole operateur;
	if (ls.getSymCour()=="+" || ls.getSymCour()=="-" )
        {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opAdd>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult() {
//  <opMult>  ::= * | /
	Symbole operateur;
	if (ls.getSymCour()=="*" || ls.getSymCour()=="/" )
        {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opMult>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme()
    // <terme> ::= <facteur> { <opMult> facteur> }
{
    Noeud* fact = facteur();
    while(ls.getSymCour()=="*" || ls.getSymCour()=="/"){
        Symbole operateur = opMult();
        Noeud* factDroit = facteur();
        fact = new NoeudOperateurMult(operateur,fact,factDroit);
    }
    return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBool(){
//  <expBool> ::= <Bool> { <opOu> <Bool> } 

	Noeud* boole = Bool();
	while (ls.getSymCour()=="ou")
                 {
		Symbole operateur = opOu();
		Noeud* booleDroit = Bool();
                boole = new NoeudOperateurOu(operateur,boole,booleDroit);
                }
        return boole;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::Bool()
    //  <Bool> ::= <relation> { <opEt> <relation>}
{
    Noeud* relat = relation();
    while(ls.getSymCour()=="et"){
        Symbole operateur = opEt();
        Noeud* relatDroit = relation();
        relat = new NoeudOperateurEt(operateur,relat,relatDroit);
    }
    return relat;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opEt() {
//  <opAdd>  ::= + | - 
	Symbole operateur;
	if (ls.getSymCour()=="et" )
        {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opEt>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu() {
//  <opMult>  ::= * | /
	Symbole operateur;
	if (ls.getSymCour()=="ou" )
        {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opOu>");
	return operateur;
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::relation()
     //  <relation> ::= <expression> { <opRel> <expression> }
{
    Noeud* exp = expression();
    while(ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<" ||
            ls.getSymCour()=="<=" || ls.getSymCour()==">" ||ls.getSymCour()==">="){
        Symbole operateur = opRel();
        Noeud* expDroit = expression();
        exp = new NoeudOperateurRel(operateur,exp,expDroit);
    }
    return exp;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel() {
      //  <opRel> ::= == | != | < | <= | > | >=
	Symbole operateur;
	if (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<" ||
            ls.getSymCour()=="<=" || ls.getSymCour()==">" ||ls.getSymCour()==">=")
        {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opRel>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opUnaire() {
      //  <opUnaire> ::= - | non
	Symbole operateur;
	if (ls.getSymCour()=="-" || ls.getSymCour()=="non" )
        {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opUnaire>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi() {
    //  <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> }[ sinon <seqInst> ] finsi
    
    NoeudInstSi* nsi = new NoeudInstSi();
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    nsi->ajoute(cond,seq);
    while (ls.getSymCour() == "sinonsi"){
        sauterSymCour("sinonsi");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        nsi->ajoute(cond,seq);

        
        if(ls.getSymCour() == "sinon"){
            sauterSymCour("sinon");
            Noeud* seq = seqInst();
            nsi->ajoute(NULL,seq);
        }
    }
    sauterSymCour("finsi");
    return nsi;

}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq()
//  <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque 
{

   sauterSymCour("tantque");
   sauterSymCour("(");
   Noeud* cond = expBool();
   sauterSymCour(")");
   Noeud* seq = seqInst();
   sauterSymCour("fintantque");
   return new NoeudInstTq(cond,seq);
        
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour()
//  <instPour> ::= pour ( <affectation> ; <expBool> ; <affectation> ) <seqInst> finpour
{
   sauterSymCour("pour");
   sauterSymCour("(");
   Noeud* affect1 = affectation();
   sauterSymCour(";");
   Noeud* cond = expBool();
   sauterSymCour(";");
   Noeud* affect2 = affectation();
   sauterSymCour(")");
   Noeud* seq = seqInst();
   sauterSymCour("finpour");
   return new NoeudInstPour(affect1,cond,affect2,seq);
        
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter()
// <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )        
{
        sauterSymCour("repeter");
        Noeud* seq = seqInst();
        sauterSymCour("jusqua");
        sauterSymCour("(");
        Noeud* exp = expBool();
        sauterSymCour(")");
        return new NoeudInstRepeter(exp,seq);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire()
// <instLire> ::= lire ( <variable> )   
{
        sauterSymCour("lire");
        sauterSymCour("(");
        testerSymCour("<VARIABLE>");
        Noeud* var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour(")");
        return new NoeudInstLire(var);
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire()
// <instEcrire> ::= ecrire ( <expression> | <chaine> ) 
{
        sauterSymCour("ecrire");
        sauterSymCour("(");
        Noeud* exp = NULL;
        if (ls.getSymCour()=="<CHAINE>") 
        {
                testerSymCour("<CHAINE>");
                exp = ts.chercheAjoute(ls.getSymCour());
                ls.suivant();
        }
        else 
        {
                 exp = expression();
        }        
        sauterSymCour(")");
        return new NoeudInstEcrire(exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSwitch() 
        // <instSwitch> ::= switch (<expression> | <ENTIER>)
        //                                                 case <expression> | <ENTIER>:
        //                                                      <seqInst>
        //                                                 { case <expression> | <ENTIER>:
        //                                                              <seqInst> }
        //                                                 default:
        //                                                      <seqInst>
        //                                                 finswitch
{   
        NoeudInstSwitch* swit;


        // switch
        sauterSymCour("switch");
        sauterSymCour("(");
                testerSymCour("<VARIABLE>");
                Noeud* var = ts.chercheAjoute(ls.getSymCour());
                ls.suivant();
        sauterSymCour(")");
        swit = new NoeudInstSwitch(var);

        // case
        while (ls.getSymCour() == "case") {
                sauterSymCour("case");
                if (ls.getSymCour() == "<ENTIER>") {
                        Noeud* cond = ts.chercheAjoute(ls.getSymCour());
                        swit->ajoutecond(cond);
                        ls.suivant();
                        
                }
                sauterSymCour(":");
                swit->ajouteseq(seqInst());
                
                sauterSymCour("break");
        }
        
        //  default 
        if (ls.getSymCour() == "default") {
                ls.suivant();
                sauterSymCour(":");
                swit->ajouteseq(seqInst());
        }
        sauterSymCour("finswitch");
        return swit;

}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
                throw except();
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	throw except();
}
